tree grammar ExpresionesEvaluator;

options {
  language = Java;
  tokenVocab = ExpresionesParser;
  ASTLabelType = MiArbol;
}

@header {
   package practica.analizadoresAntlr;
   import java.util.ArrayList;
   import practica.arboles.*;
   import java.util.Hashtable;
   import java.util.Map;
   import java.util.HashMap;
}

@members {

  Hashtable<String, ArrayList<Float>> variables = new Hashtable<String, ArrayList<Float>> ();
  private ArrayList<MiArbol> funciones;
  private final Map<String, Float> valoresSubArbol = new HashMap<String, Float>();
  private int index = 0;
  private int indexError = -1;
  
  public ExpresionesEvaluator(CommonTreeNodeStream nodes, ArrayList<MiArbol> funciones) {
      this(nodes);
      this.funciones = funciones;
  }

  private ExpresionesEvaluator(MiArbol funcion, ArrayList<MiArbol> funciones, Hashtable<String, ArrayList<Float>> variables, Float parametro) {
    this(new CommonTreeNodeStream(funcion.getChild(2)), funciones);
    this.variables = variables;
    valoresSubArbol.put(funcion.getChild(1).getText(), parametro);
  }
  
  //Funcion para buscar por nombre una funcion en la lista de funciones
  private MiArbol buscarFuncion(String n, Float parametro) {
    for (MiArbol f : this.funciones) {
      if (f.getChild(0).getText().equals(n)) {
        MiArbol id = (MiArbol) f.getChild(1);
        if ((id.getToken().getType() != NUMERO) || (new Float(id.getToken().getText()).equals(parametro))) 
          return f;
      }
    }
    return null;
  }
  
  //Funcion para obtener el valor de una variable
  public Float getValor(String n) {
    Float valor = valoresSubArbol.get(n);
    if (valor != null)
      return valor;
    else {
      valor = variables.get(n).get(index);
      if (valor != null)
        return valor;
    }
    return new Float(0);
  }
  
}

//------------------------------------------------------------------------------
// Tree Parser Rules
//------------------------------------------------------------------------------

entrada returns [ArrayList<Float> value = new ArrayList<Float>()]
  : ^(LISTA_INST (a = sentence { if (!a.isNaN()) value.add(a); })*)
  ;
  
sentence returns [Float value = new Float(Float.NaN)]
  : ^(ASIGNACION ID expr) { ArrayList<Float> aux = variables.get($ID.text);
                            if (aux == null)
                              aux = new ArrayList<Float>();
                            aux.add(0, new Float($expr.value));
                            variables.put($ID.text, aux); 
                            System.out.println("Resultado: " + $ID.text + " = " + $expr.value);
                            $ASIGNACION.setValor($expr.value);
                            $value = new Float($expr.value); }
  | expr { System.out.println("Resultado: " + $expr.value); $value = new Float($expr.value); }
  | fundef
  ;
  
expr returns [float value = 0]
  : ^(MAS a = expr b = expr) { $value = a + b;
                                $MAS.setValor(a + b); }
    | ^(MENOS a = expr b = expr) { $value = a - b;
                                   $MENOS.setValor(a - b); }
    | ^(CAMBIO_SIGNO a = expr) { $value = -a; 
                              $CAMBIO_SIGNO.setValor(-a); }
    | ^(ASTERISCO a = expr b = expr) { $value = a * b;
                                      $ASTERISCO.setValor(a * b); }
    | ^(SLASH a = expr b = expr) { $value = a / b;
                                   $SLASH.setValor(a / b); }
    | ID { if (variables.get($ID.text).size() > index) {
             Float v = getValor($ID.text);
             $value = v; 
             $ID.setValor(v);
           } else { //Hemos intentado acceder a un valor inexistente
             System.out.println("Error de acceso a una valor inexistente");
             Tree childNode = $ID.getParent();
             for (int aux = index - 1; aux > 1; aux--)
               childNode = childNode.getParent();
             Tree parent = childNode.getParent();
             parent.deleteChild(childNode.getChildIndex());
           }
         }
    | NUMERO { $value = Float.parseFloat($NUMERO.text);
                $NUMERO.setValor($value); }
    | ^(ABRIRCORCHETE { index++; } a = expr { index--; }) { $value = a;
                                                         $ABRIRCORCHETE.setValor(a);
                                                       }
    | funcall { $value = $funcall.value; }
  ;
  catch [ArithmeticException ae] {
    $value = 0;
  }

fundef : ^(DEF_FUNCION ID .+) { $DEF_FUNCION.setLinea($ID.getLine());
                           $DEF_FUNCION.setColumna($ID.getColumna()); }
       ;

funcall returns [float value = 0]
  : ^(LLAMADA_FUN ID a = expr) { 
                             MiArbol raiz = buscarFuncion($ID.text, a);
                             if (raiz == null)
                               System.out.println("No se encuentra " + $ID.text + "(" + a + ")");
                             else {
                               ExpresionesEvaluator aa = new ExpresionesEvaluator(raiz, funciones, variables, a);
                               $value = aa.expr();
                             }
                             $LLAMADA_FUN.setValor($value);
                             $LLAMADA_FUN.setLinea($ID.getLinea());
                             $LLAMADA_FUN.setColumna($ID.getColumna());
                           }
  ;